﻿/*
Copyright (c) 2012 Eric Lasota
Copyright (c) 1997 Jung woo-jae

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

using System;
using System.Collections.Generic;

using System.Text;

namespace MPEGnet
{
    class MP2Dec
    {
        public static uint BITMASK(int offset, int length)
        {
            return ((~(0xffffffff << (length))) << (offset));
        }

        public const int MAXSUBBAND = 32;
        public const int MAXCHANNEL = 2;
        public const int SCALEBLOCK = 12;
        public const int CALCBUFFERSIZE = 512;
        public const int SCALE = 32768;
        public const int MAXSCALE = (SCALE - 1);
        public const int MINSCALE = (-SCALE);

        public static FixedReal[] filter = FixedReal.FromDoubles(new double[]
        {
           0.000000000, -0.000442505,  0.003250122, -0.007003784,
           0.031082153, -0.078628540,  0.100311279, -0.572036743,
           1.144989014,  0.572036743,  0.100311279,  0.078628540,
           0.031082153,  0.007003784,  0.003250122,  0.000442505,
          -0.000015259, -0.000473022,  0.003326416, -0.007919312,
           0.030517578, -0.084182739,  0.090927124, -0.600219727,
           1.144287109,  0.543823242,  0.108856201,  0.073059082,
           0.031478882,  0.006118774,  0.003173828,  0.000396729,
          -0.000015259, -0.000534058,  0.003387451, -0.008865356,
           0.029785156, -0.089706421,  0.080688477, -0.628295898,
           1.142211914,  0.515609741,  0.116577148,  0.067520142,
           0.031738281,  0.005294800,  0.003082275,  0.000366211,
          -0.000015259, -0.000579834,  0.003433228, -0.009841919,
           0.028884888, -0.095169067,  0.069595337, -0.656219482,
           1.138763428,  0.487472534,  0.123474121,  0.061996460,
           0.031845093,  0.004486084,  0.002990723,  0.000320435,
          -0.000015259, -0.000625610,  0.003463745, -0.010848999,
           0.027801514, -0.100540161,  0.057617188, -0.683914185,
           1.133926392,  0.459472656,  0.129577637,  0.056533813,
           0.031814575,  0.003723145,  0.002899170,  0.000289917,
          -0.000015259, -0.000686646,  0.003479004, -0.011886597,
           0.026535034, -0.105819702,  0.044784546, -0.711318970,
           1.127746582,  0.431655884,  0.134887695,  0.051132202,
           0.031661987,  0.003005981,  0.002792358,  0.000259399,
          -0.000015259, -0.000747681,  0.003479004, -0.012939453,
           0.025085449, -0.110946655,  0.031082153, -0.738372803,
           1.120223999,  0.404083252,  0.139450073,  0.045837402,
           0.031387329,  0.002334595,  0.002685547,  0.000244141,
          -0.000030518, -0.000808716,  0.003463745, -0.014022827,
           0.023422241, -0.115921021,  0.016510010, -0.765029907,
           1.111373901,  0.376800537,  0.143264771,  0.040634155,
           0.031005859,  0.001693726,  0.002578735,  0.000213623,
          -0.000030518, -0.000885010,  0.003417969, -0.015121460,
           0.021575928, -0.120697021,  0.001068115, -0.791213989,
           1.101211548,  0.349868774,  0.146362305,  0.035552979,
           0.030532837,  0.001098633,  0.002456665,  0.000198364,
          -0.000030518, -0.000961304,  0.003372192, -0.016235352,
           0.019531250, -0.125259399, -0.015228271, -0.816864014,
           1.089782715,  0.323318481,  0.148773193,  0.030609131,
           0.029937744,  0.000549316,  0.002349854,  0.000167847,
          -0.000030518, -0.001037598,  0.003280640, -0.017349243,
           0.017257690, -0.129562378, -0.032379150, -0.841949463,
           1.077117920,  0.297210693,  0.150497437,  0.025817871,
           0.029281616,  0.000030518,  0.002243042,  0.000152588,
          -0.000045776, -0.001113892,  0.003173828, -0.018463135,
           0.014801025, -0.133590698, -0.050354004, -0.866363525,
           1.063217163,  0.271591187,  0.151596069,  0.021179199,
           0.028533936, -0.000442505,  0.002120972,  0.000137329,
          -0.000045776, -0.001205444,  0.003051758, -0.019577026,
           0.012115479, -0.137298584, -0.069168091, -0.890090942,
           1.048156738,  0.246505737,  0.152069092,  0.016708374,
           0.027725220, -0.000869751,  0.002014160,  0.000122070,
          -0.000061035, -0.001296997,  0.002883911, -0.020690918,
           0.009231567, -0.140670776, -0.088775635, -0.913055420,
           1.031936646,  0.221984863,  0.151962280,  0.012420654,
           0.026840210, -0.001266479,  0.001907349,  0.000106812,
          -0.000061035, -0.001388550,  0.002700806, -0.021789551,
           0.006134033, -0.143676758, -0.109161377, -0.935195923,
           1.014617920,  0.198059082,  0.151306152,  0.008316040,
           0.025909424, -0.001617432,  0.001785278,  0.000106812,
          -0.000076294, -0.001480103,  0.002487183, -0.022857666,
           0.002822876, -0.146255493, -0.130310059, -0.956481934,
           0.996246338,  0.174789429,  0.150115967,  0.004394531,
           0.024932861, -0.001937866,  0.001693726,  0.000091553,
          -0.000076294, -0.001586914,  0.002227783, -0.023910522,
          -0.000686646, -0.148422241, -0.152206421, -0.976852417,
           0.976852417,  0.152206421,  0.148422241,  0.000686646,
           0.023910522, -0.002227783,  0.001586914,  0.000076294,
          -0.000091553, -0.001693726,  0.001937866, -0.024932861,
          -0.004394531, -0.150115967, -0.174789429, -0.996246338,
           0.956481934,  0.130310059,  0.146255493, -0.002822876,
           0.022857666, -0.002487183,  0.001480103,  0.000076294,
          -0.000106812, -0.001785278,  0.001617432, -0.025909424,
          -0.008316040, -0.151306152, -0.198059082, -1.014617920,
           0.935195923,  0.109161377,  0.143676758, -0.006134033,
           0.021789551, -0.002700806,  0.001388550,  0.000061035,
          -0.000106812, -0.001907349,  0.001266479, -0.026840210,
          -0.012420654, -0.151962280, -0.221984863, -1.031936646,
           0.913055420,  0.088775635,  0.140670776, -0.009231567,
           0.020690918, -0.002883911,  0.001296997,  0.000061035,
          -0.000122070, -0.002014160,  0.000869751, -0.027725220,
          -0.016708374, -0.152069092, -0.246505737, -1.048156738,
           0.890090942,  0.069168091,  0.137298584, -0.012115479,
           0.019577026, -0.003051758,  0.001205444,  0.000045776,
          -0.000137329, -0.002120972,  0.000442505, -0.028533936,
          -0.021179199, -0.151596069, -0.271591187, -1.063217163,
           0.866363525,  0.050354004,  0.133590698, -0.014801025,
           0.018463135, -0.003173828,  0.001113892,  0.000045776,
          -0.000152588, -0.002243042, -0.000030518, -0.029281616,
          -0.025817871, -0.150497437, -0.297210693, -1.077117920,
           0.841949463,  0.032379150,  0.129562378, -0.017257690,
           0.017349243, -0.003280640,  0.001037598,  0.000030518,
          -0.000167847, -0.002349854, -0.000549316, -0.029937744,
          -0.030609131, -0.148773193, -0.323318481, -1.089782715,
           0.816864014,  0.015228271,  0.125259399, -0.019531250,
           0.016235352, -0.003372192,  0.000961304,  0.000030518,
          -0.000198364, -0.002456665, -0.001098633, -0.030532837,
          -0.035552979, -0.146362305, -0.349868774, -1.101211548,
           0.791213989, -0.001068115,  0.120697021, -0.021575928,
           0.015121460, -0.003417969,  0.000885010,  0.000030518,
          -0.000213623, -0.002578735, -0.001693726, -0.031005859,
          -0.040634155, -0.143264771, -0.376800537, -1.111373901,
           0.765029907, -0.016510010,  0.115921021, -0.023422241,
           0.014022827, -0.003463745,  0.000808716,  0.000030518,
          -0.000244141, -0.002685547, -0.002334595, -0.031387329,
          -0.045837402, -0.139450073, -0.404083252, -1.120223999,
           0.738372803, -0.031082153,  0.110946655, -0.025085449,
           0.012939453, -0.003479004,  0.000747681,  0.000015259,
          -0.000259399, -0.002792358, -0.003005981, -0.031661987,
          -0.051132202, -0.134887695, -0.431655884, -1.127746582,
           0.711318970, -0.044784546,  0.105819702, -0.026535034,
           0.011886597, -0.003479004,  0.000686646,  0.000015259,
          -0.000289917, -0.002899170, -0.003723145, -0.031814575,
          -0.056533813, -0.129577637, -0.459472656, -1.133926392,
           0.683914185, -0.057617188,  0.100540161, -0.027801514,
           0.010848999, -0.003463745,  0.000625610,  0.000015259,
          -0.000320435, -0.002990723, -0.004486084, -0.031845093,
          -0.061996460, -0.123474121, -0.487472534, -1.138763428,
           0.656219482, -0.069595337,  0.095169067, -0.028884888,
           0.009841919, -0.003433228,  0.000579834,  0.000015259,
          -0.000366211, -0.003082275, -0.005294800, -0.031738281,
          -0.067520142, -0.116577148, -0.515609741, -1.142211914,
           0.628295898, -0.080688477,  0.089706421, -0.029785156,
           0.008865356, -0.003387451,  0.000534058,  0.000015259,
          -0.000396729, -0.003173828, -0.006118774, -0.031478882,
          -0.073059082, -0.108856201, -0.543823242, -1.144287109,
           0.600219727, -0.090927124,  0.084182739, -0.030517578,
           0.007919312, -0.003326416,  0.000473022,  0.000015259
        } );

        public static FixedReal[] scalefactorstable = new FixedReal[64];

        // These are all padded out so that out-of-bounds codes don't cause errors
        public static FixedReal[] group5bits = new FixedReal[32 * 3];   // [27]
        public static FixedReal[] group7bits = new FixedReal[128 * 3];  // [125]
        public static FixedReal[] group10bits = new FixedReal[1024 * 3]; // [729]

        public static FixedReal[][] grouptableA =
            { null,group5bits,group7bits,group10bits,null,null,null,null,null,null,null,null,null,null,null,null};
        public static FixedReal[][] grouptableB1 =
            { null,group5bits,null,null,null,null,null,null,null,null,null,null,null,null,null,null };
        public static FixedReal[][] grouptableB234 =
            { null,group5bits,group7bits,null,group10bits,null,null,null,null,null,null,null,null,null,null,null};

        public static uint[] codelengthtableA =
            { 0, 5, 7, 10, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
        public static uint[] codelengthtableB1 =
            { 0, 5, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
        public static uint[] codelengthtableB2 =
            { 0, 5, 7, 3, 10, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16 };
        public static uint[] codelengthtableB3 = { 0, 5, 7, 3, 10, 4, 5, 16 };
        public static uint[] codelengthtableB4 = { 0, 5, 7, 16 };


        public static FixedReal[] factortableA = FixedReal.FromDoubles(new double[]
        { 0.0,        1.0/2.0,    1.0/4.0,    1.0/8.0,
          1.0/8.0,    1.0/16.0,   1.0/32.0,   1.0/64.0,
          1.0/128.0,  1.0/256.0,  1.0/512.0,  1.0/1024.0,
          1.0/2048.0, 1.0/4096.0, 1.0/8192.0, 1.0/16384.0 } );
        public static FixedReal[] factortableB1 = FixedReal.FromDoubles(new double[]
        { 0.0,        1.0/2.0,    1.0/4.0,     1.0/8.0,
          1.0/16.0,   1.0/32.0,   1.0/64.0,    1.0/128.0,
          1.0/256.0,  1.0/512.0,  1.0/1024.0,  1.0/2048.0,
          1.0/4096.0, 1.0/8192.0, 1.0/16384.0, 1.0/32768.0 } );
        public static FixedReal[] factortableB2 = FixedReal.FromDoubles(new double[]
        { 0.0,        1.0/2.0,    1.0/4.0,    1.0/4.0,
          1.0/8.0,    1.0/8.0,    1.0/16.0,   1.0/32.0,
          1.0/64.0,   1.0/128.0,  1.0/256.0,  1.0/512.0,
          1.0/1024.0, 1.0/2048.0, 1.0/4096.0, 1.0/32768.0 } );

        public static FixedReal[] factortableB3 = FixedReal.FromDoubles(new double[]
            { 0.0, 1.0/2.0, 1.0/4.0, 1.0/4.0, 1.0/8.0, 1.0/8.0, 1.0/16.0, 1.0/32768.0 } );
        public static FixedReal[] factortableB4 = FixedReal.FromDoubles(new double[] { 0.0, 1.0 / 2.0, 1.0 / 4.0, 1.0 / 32768.0 });


        public static FixedReal[] ctableA = FixedReal.FromDoubles(new double[]
        {           0.0, 1.33333333333, 1.60000000000, 1.77777777777,
          1.06666666666, 1.03225806452, 1.01587301587, 1.00787401575,
          1.00392156863, 1.00195694716, 1.00097751711, 1.00048851979,
          1.00024420024, 1.00012208522, 1.00006103888, 1.00003051851} );
        public static FixedReal[] ctableB1 = FixedReal.FromDoubles(new double[]
        {           0.0, 1.33333333333, 1.14285714286, 1.06666666666,
          1.03225806452, 1.01587301587, 1.00787401575, 1.00392156863,
          1.00195694716, 1.00097751711, 1.00048851979, 1.00024420024,
          1.00012208522, 1.00006103888, 1.00003051851, 1.00001525902} );
        public static FixedReal[] ctableB2 = FixedReal.FromDoubles(new double[]
        {           0.0, 1.33333333333, 1.60000000000, 1.14285714286,
          1.77777777777, 1.06666666666, 1.03225806452, 1.01587301587,
          1.00787401575, 1.00392156863, 1.00195694716, 1.00097751711,
          1.00048851979, 1.00024420024, 1.00012208522, 1.00001525902} );
        public static FixedReal[] ctableB3 = FixedReal.FromDoubles(new double[]
        {           0.0, 1.33333333333, 1.60000000000, 1.14285714286,
          1.77777777777, 1.06666666666, 1.03225806452, 1.00001525902 } );
        public static FixedReal[] ctableB4 = FixedReal.FromDoubles(new double[]
        { 0.0, 1.33333333333, 1.60000000000, 1.00001525902 } );


        public static FixedReal[] dtableA = FixedReal.FromDoubles(new double[]
        {           0.0, 0.50000000000, 0.50000000000, 0.50000000000,
          0.12500000000, 0.06250000000, 0.03125000000, 0.01562500000,
          0.00781250000, 0.00390625000, 0.00195312500, 0.00097656250,
          0.00048828125, 0.00024414063, 0.00012207031, 0.00006103516} );

        public static FixedReal[] dtableB1 = FixedReal.FromDoubles(new double[]
        {           0.0, 0.50000000000, 0.25000000000, 0.12500000000,
          0.06250000000, 0.03125000000, 0.01562500000, 0.00781250000,
          0.00390625000, 0.00195312500, 0.00097656250, 0.00048828125,
          0.00024414063, 0.00012207031, 0.00006103516, 0.00003051758} );

        public static FixedReal[] dtableB2 = FixedReal.FromDoubles(new double[]
        { 0.0,           0.50000000000, 0.50000000000, 0.25000000000,
          0.50000000000, 0.12500000000, 0.06250000000, 0.03125000000,
          0.01562500000, 0.00781250000, 0.00390625000, 0.00195312500,
          0.00097656250, 0.00048828125, 0.00024414063, 0.00003051758} );

        public static FixedReal[] dtableB3 = FixedReal.FromDoubles(new double[]
        {           0.0, 0.50000000000, 0.50000000000, 0.25000000000,
          0.50000000000, 0.12500000000, 0.06250000000, 0.00003051758} );

        public static FixedReal[] dtableB4 = FixedReal.FromDoubles(new double[]
        {0.0, 0.50000000000, 0.50000000000, 0.00003051758} );

        public static FixedReal[] hcos_64 = new FixedReal[16];
        public static FixedReal[] hcos_32 = new FixedReal[8];
        public static FixedReal[] hcos_16 = new FixedReal[4];
        public static FixedReal[] hcos_8 = new FixedReal[2];
        public static FixedReal hcos_4;

        struct OutIO
        {
            FixedReal[] _buffer1;
            int _offset1;
            FixedReal[] _buffer2;
            int _offset2;

            public OutIO(FixedReal[] buffer1, int offset1, FixedReal[] buffer2, int offset2)
            {
                _buffer1 = buffer1;
                _offset1 = offset1;
                _buffer2 = buffer2;
                _offset2 = offset2;
            }

            public void OUT1(int v, FixedReal t)
            {
                this._buffer1[_offset1 + (32-(v))*16] = -t;
                this._buffer1[_offset1 + (v)*16] = t;
            }

            public void OUT2(int v, FixedReal t)
            {
                this._buffer2[_offset2 + (96-(v)-32)*16] = t;
                this._buffer2[_offset2 + ((v)-32)*16] = t;
            }

            public FixedReal get1(int idx)
            {
                return this._buffer1[_offset1 + idx];
            }

            public void set1(int idx, FixedReal v)
            {
                this._buffer1[_offset1 + idx] = v;
            }

            public FixedReal get2(int idx)
            {
                return this._buffer2[_offset2 + idx];
            }

            public void set2(int idx, FixedReal v)
            {
                this._buffer2[_offset2 + idx] = v;
            }
        }

        static void computebuffer(FixedReal[] fraction, FixedReal[][] buffer, int currentcalcbuffer, int calcbufferoffset)
        {
	        FixedReal p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,pa,pb,pc,pd,pe,pf;
	        FixedReal q0,q1,q2,q3,q4,q5,q6,q7,q8,q9,qa,qb,qc,qd,qe,qf;
	        OutIO o = new OutIO(buffer[currentcalcbuffer], calcbufferoffset, buffer[currentcalcbuffer^1], calcbufferoffset);

            // compute new values via a fast cosine transform:
            {
                FixedReal[] x = fraction;

                p0=x[ 0]+x[31];p1=x[ 1]+x[30];p2=x[ 2]+x[29];p3=x[ 3]+x[28];
                p4=x[ 4]+x[27];p5=x[ 5]+x[26];p6=x[ 6]+x[25];p7=x[ 7]+x[24];
                p8=x[ 8]+x[23];p9=x[ 9]+x[22];pa=x[10]+x[21];pb=x[11]+x[20];
                pc=x[12]+x[19];pd=x[13]+x[18];pe=x[14]+x[17];pf=x[15]+x[16];
            }


            q0=p0+pf;q1=p1+pe;q2=p2+pd;q3=p3+pc;
            q4=p4+pb;q5=p5+pa;q6=p6+p9;q7=p7+p8;
            q8=hcos_32[0]*(p0-pf);q9=hcos_32[1]*(p1-pe);
            qa=hcos_32[2]*(p2-pd);qb=hcos_32[3]*(p3-pc);
            qc=hcos_32[4]*(p4-pb);qd=hcos_32[5]*(p5-pa);
            qe=hcos_32[6]*(p6-p9);qf=hcos_32[7]*(p7-p8);

            p0=q0+q7;p1=q1+q6;p2=q2+q5;p3=q3+q4;
            p4=hcos_16[0]*(q0-q7);p5=hcos_16[1]*(q1-q6);
            p6=hcos_16[2]*(q2-q5);p7=hcos_16[3]*(q3-q4);
            p8=q8+qf;p9=q9+qe;pa=qa+qd;pb=qb+qc;
            pc=hcos_16[0]*(q8-qf);pd=hcos_16[1]*(q9-qe);
            pe=hcos_16[2]*(qa-qd);pf=hcos_16[3]*(qb-qc);

            q0=p0+p3;q1=p1+p2;q2=hcos_8[0]*(p0-p3);q3=hcos_8[1]*(p1-p2);
            q4=p4+p7;q5=p5+p6;q6=hcos_8[0]*(p4-p7);q7=hcos_8[1]*(p5-p6);
            q8=p8+pb;q9=p9+pa;qa=hcos_8[0]*(p8-pb);qb=hcos_8[1]*(p9-pa);
            qc=pc+pf;qd=pd+pe;qe=hcos_8[0]*(pc-pf);qf=hcos_8[1]*(pd-pe);

            p0=q0+q1;p1=hcos_4*(q0-q1);p2=q2+q3;p3=hcos_4*(q2-q3);
            p4=q4+q5;p5=hcos_4*(q4-q5);p6=q6+q7;p7=hcos_4*(q6-q7);
            p8=q8+q9;p9=hcos_4*(q8-q9);pa=qa+qb;pb=hcos_4*(qa-qb);
            pc=qc+qd;pd=hcos_4*(qc-qd);pe=qe+qf;pf=hcos_4*(qe-qf);

            {
                FixedReal tmp;

                tmp=p6+p7;
                o.OUT2(36,-(p5+tmp));
                o.OUT2(44,-(p4+tmp));
                tmp=pb+pf;
                o.OUT1(10,tmp);
                o.OUT1(6,pd+tmp);
                tmp=pe+pf;
                o.OUT2(46,-(p8+pc+tmp));
                o.OUT2(34,-(p9+pd+tmp));
                tmp+=pa+pb;
                o.OUT2(38,-(pd+tmp));
                o.OUT2(42,-(pc+tmp));
                o.OUT1(2,p9+pd+pf);
                o.OUT1(4,p5+p7);
                o.OUT2(48,-p0);
                //out2[0]=-(out1[0]=p1);
                o.set2(0, -p1);
                o.set1(0, p1);
                o.OUT1( 8,p3);
                o.OUT1(12,p7);
                o.OUT1(14,pf);
                o.OUT2(40,-(p2+p3));
            }

            {
                FixedReal[] x=fraction;

                p0=hcos_64[ 0]*(x[ 0]-x[31]);p1=hcos_64[ 1]*(x[ 1]-x[30]);
                p2=hcos_64[ 2]*(x[ 2]-x[29]);p3=hcos_64[ 3]*(x[ 3]-x[28]);
                p4=hcos_64[ 4]*(x[ 4]-x[27]);p5=hcos_64[ 5]*(x[ 5]-x[26]);
                p6=hcos_64[ 6]*(x[ 6]-x[25]);p7=hcos_64[ 7]*(x[ 7]-x[24]);
                p8=hcos_64[ 8]*(x[ 8]-x[23]);p9=hcos_64[ 9]*(x[ 9]-x[22]);
                pa=hcos_64[10]*(x[10]-x[21]);pb=hcos_64[11]*(x[11]-x[20]);
                pc=hcos_64[12]*(x[12]-x[19]);pd=hcos_64[13]*(x[13]-x[18]);
                pe=hcos_64[14]*(x[14]-x[17]);pf=hcos_64[15]*(x[15]-x[16]);
            }

            q0=p0+pf;q1=p1+pe;q2=p2+pd;q3=p3+pc;
            q4=p4+pb;q5=p5+pa;q6=p6+p9;q7=p7+p8;
            q8=hcos_32[0]*(p0-pf);q9=hcos_32[1]*(p1-pe);
            qa=hcos_32[2]*(p2-pd);qb=hcos_32[3]*(p3-pc);
            qc=hcos_32[4]*(p4-pb);qd=hcos_32[5]*(p5-pa);
            qe=hcos_32[6]*(p6-p9);qf=hcos_32[7]*(p7-p8);

            p0=q0+q7;p1=q1+q6;p2=q2+q5;p3=q3+q4;
            p4=hcos_16[0]*(q0-q7);p5=hcos_16[1]*(q1-q6);
            p6=hcos_16[2]*(q2-q5);p7=hcos_16[3]*(q3-q4);
            p8=q8+qf;p9=q9+qe;pa=qa+qd;pb=qb+qc;
            pc=hcos_16[0]*(q8-qf);pd=hcos_16[1]*(q9-qe);
            pe=hcos_16[2]*(qa-qd);pf=hcos_16[3]*(qb-qc);

            q0=p0+p3;q1=p1+p2;q2=hcos_8[0]*(p0-p3);q3=hcos_8[1]*(p1-p2);
            q4=p4+p7;q5=p5+p6;q6=hcos_8[0]*(p4-p7);q7=hcos_8[1]*(p5-p6);
            q8=p8+pb;q9=p9+pa;qa=hcos_8[0]*(p8-pb);qb=hcos_8[1]*(p9-pa);
            qc=pc+pf;qd=pd+pe;qe=hcos_8[0]*(pc-pf);qf=hcos_8[1]*(pd-pe);

            p0=q0+q1;p1=hcos_4*(q0-q1);
            p2=q2+q3;p3=hcos_4*(q2-q3);
            p4=q4+q5;p5=hcos_4*(q4-q5);
            p6=q6+q7;p7=hcos_4*(q6-q7);
            p8=q8+q9;p9=hcos_4*(q8-q9);
            pa=qa+qb;pb=hcos_4*(qa-qb);
            pc=qc+qd;pd=hcos_4*(qc-qd);
            pe=qe+qf;pf=hcos_4*(qe-qf);

            {
                FixedReal tmp;

                tmp=pd+pf;
                o.OUT1(5,p5+p7+pb+tmp);
                tmp+=p9;
                o.OUT1(1,p1+tmp);
                o.OUT2(33,-(p1+pe+tmp));
                tmp+=p5+p7;
                o.OUT1(3,tmp);
                o.OUT2(35,-(p6+pe+tmp));
                tmp=pa+pb+pc+pd+pe+pf;
                o.OUT2(39,-(p2+p3+tmp-pc));
                o.OUT2(43,-(p4+p6+p7+tmp-pd));
                o.OUT2(37,-(p5+p6+p7+tmp-pc));
                o.OUT2(41,-(p2+p3+tmp-pd));
                tmp=p8+pc+pe+pf;
                o.OUT2(47,-(p0+tmp));
                o.OUT2(45,-(p4+p6+p7+tmp));
                tmp=pb+pf;
                o.OUT1(11,p7+tmp);
                tmp+=p3;
                o.OUT1( 9,tmp);
                o.OUT1( 7,pd+tmp);
                o.OUT1(13,p7+pf);
                o.OUT1(15,pf);
            }
        }

        struct generate_state
        {
            public FixedReal _r;
            public int _scalefactor;
            public short[] _output;
            public int _outputOffset;

            public FixedReal[] _vp;
            public int _vpOffset;
            public FixedReal[] _dp;
            public int _dpOffset;

            public void SAVE()
            {
		        int raw = _r.MulAndRound(_scalefactor);
                if(raw > MAXSCALE)
                    raw = MAXSCALE;
                else if(raw < MINSCALE)
                    raw = MINSCALE;
                _output[_outputOffset] = (short)raw;
		        _outputOffset++;
            }

            public void OS()
            {
                _r = _vp[_vpOffset] * _dp[_dpOffset];
                _dpOffset++;
            }

            public void XX()
            {
                _vpOffset +=15;
                _r += _vp[_vpOffset] * _dp[_dpOffset];
                _dpOffset++;
            }

            public void OP()
            {
                _vpOffset--;
                _r += _vp[_vpOffset] * _dp[_dpOffset];
                _dpOffset++;
            }
        }

        // Returns modified output offset
        static int generate(int currentcalcbuffer, int calcbufferoffset, FixedReal[][] calcbuffer, int scalefactor,
	        short[] output, int outputOffset)
        {
            int i;

            generate_state g;
            g._dp = filter;
            g._dpOffset = 0;
            g._vp = calcbuffer[currentcalcbuffer];
            g._vpOffset = calcbufferoffset;
            g._scalefactor = scalefactor;
            g._output = output;
            g._outputOffset = outputOffset;
            g._r = new FixedReal(0.0);


            i = 32;
            // actual_v+actual_write_pos;

            switch (calcbufferoffset)
            {
                case  0:for(;i!=0;i--,g._vpOffset+=15){
                          g.OS();g.XX();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();
                      g.SAVE();}break;
                case  1:for(;i!=0;i--,g._vpOffset+=15){
                      g.OS();g.OP();g.XX();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();
                      g.SAVE();}break;
                case  2:for(;i!=0;i--,g._vpOffset+=15){
                          g.OS();g.OP();g.OP();g.XX();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();
                      g.SAVE();}break;
                case  3:for(;i!=0;i--,g._vpOffset+=15){
                      g.OS();g.OP();g.OP();g.OP();g.XX();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();
                      g.SAVE();}break;
                case  4:for(;i!=0;i--,g._vpOffset+=15){
                          g.OS();g.OP();g.OP();g.OP();g.OP();g.XX();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();
                      g.SAVE();}break;
                case  5:for(;i!=0;i--,g._vpOffset+=15){
                      g.OS();g.OP();g.OP();g.OP();g.OP();g.OP();g.XX();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();
                      g.SAVE();}break;
                case  6:for(;i!=0;i--,g._vpOffset+=15){
                      g.OS();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.XX();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();
                      g.SAVE();}break;
                case  7:for(;i!=0;i--,g._vpOffset+=15){
                      g.OS();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.XX();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();
                      g.SAVE();}break;
                case  8:for(;i!=0;i--,g._vpOffset+=15){
                          g.OS();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.XX();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();
                      g.SAVE();}break;
                case  9:for(;i!=0;i--,g._vpOffset+=15){
                      g.OS();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.XX();g.OP();g.OP();g.OP();g.OP();g.OP();
                      g.SAVE();}break;
                case 10:for(;i!=0;i--,g._vpOffset+=15){
                          g.OS();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.XX();g.OP();g.OP();g.OP();g.OP();
                      g.SAVE();}break;
                case 11:for(;i!=0;i--,g._vpOffset+=15){
                          g.OS();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.XX();g.OP();g.OP();g.OP();
                      g.SAVE();}break;
                case 12:for(;i!=0;i--,g._vpOffset+=15){
                      g.OS();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.XX();g.OP();g.OP();
                      g.SAVE();}break;
                case 13:for(;i!=0;i--,g._vpOffset+=15){
                      g.OS();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.XX();g.OP();
                      g.SAVE();}break;
                case 14:for(;i!=0;i--,g._vpOffset+=15){
                      g.OS();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.XX();
                      g.SAVE();}break;
                case 15:for(;i!=0;i--,g._vpOffset+=31){
                          g.OS();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();g.OP();
                      g.SAVE();}break;
            }

            return g._outputOffset;
        }


        public static void subbandsynthesis(FixedReal[] fraction, ref int currentcalcbuffer, ref int calcbufferoffset, FixedReal[][] calcbuffer, short[] output, ref int outputOffset)
        {
            computebuffer(fraction, calcbuffer, currentcalcbuffer, calcbufferoffset);
	        outputOffset = generate(currentcalcbuffer, calcbufferoffset, calcbuffer, SCALE, output, outputOffset);

	        if(calcbufferoffset < 15)
		        calcbufferoffset++;
	        else
		        calcbufferoffset = 0;

	        currentcalcbuffer ^=1;
        }

        // For header
        public enum MpegVersion
        {
	        Mpeg2 = 0,
	        Mpeg1 = 1
        }

        public enum MpegMode
        {
	        FullStereo = 0,
	        Joint = 1,
	        Dual = 2,
	        Single = 3
        }

        public static int[,] tbl_frequencies=
        {
	        {22050,24000,16000}, // MPEG 2
	        {44100,48000,32000}  // MPEG 1
        };

        public static int[,,] tbl_bitrates=
        {
	        // MPEG 2
	        {{0,32,48,56,64,80,96,112,128,144,160,176,192,224,256},
	         {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160},
	         {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160}},

	        // MPEG 1
	        {{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448},
	         {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384},
	         {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320}}
        };

        public static int[,] tbl_bitalloclength=
        {{4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3, 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},
         {4,4,4,4,4,4,4,4,4,4,4,3,3,3,3,3, 3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2}};

        class mp2dec_static_init
        {
            public void Touch()
            {
            }

	        public mp2dec_static_init()
	        {
		        FixedReal[] group5base = FixedReal.FromDoubles( new double[] { -2.0/3.0, 0.0, 2.0/3.0 } );
		        FixedReal[] group7base = FixedReal.FromDoubles( new double[] { -0.8, -0.4, 0.0, 0.4, 0.8 } );
		        FixedReal[] group10base = FixedReal.FromDoubles( new double[] { -8.0/9.0, -6.0/9.0, -4.0/9.0, -2.0/9.0, 0.0, 2.0/9.0, 4.0/9.0, 6.0/9.0, 8.0/9.0 } );

		        int i;

		        for(i=0;i<16;i++) hcos_64[i] = new FixedReal
				        (1.0/(2.0*Math.Cos(Math.PI*(double)(i*2+1)/64.0)));
		        for(i=0;i< 8;i++) hcos_32[i] = new FixedReal
				        (1.0/(2.0*Math.Cos(Math.PI*(double)(i*2+1)/32.0)));
		        for(i=0;i< 4;i++) hcos_16[i] = new FixedReal
				        (1.0/(2.0*Math.Cos(Math.PI*(double)(i*2+1)/16.0)));
		        for(i=0;i< 2;i++) hcos_8 [i] = new FixedReal
				        (1.0/(2.0*Math.Cos(Math.PI*(double)(i*2+1)/ 8.0)));
		        hcos_4 = new FixedReal(1.0f / (2.0f * Math.Cos( Math.PI * 1.0 / 4.0 )));

		        for(i=0;i<64;i++)
			        scalefactorstable[i] = new FixedReal(2.0 * Math.Pow(0.5, (double)i / 3.0));


		        for(i=0;i<27;i++)
		        {
			        group5bits[i*3+0] = group5base[i%3];
			        group5bits[i*3+1] = group5base[(i/3)%3];
			        group5bits[i*3+2] = group5base[i/9];
		        }
		        for(i=0;i<125;i++)
		        {
			        group7bits[i*3+0] = group7base[i%5];
			        group7bits[i*3+1] = group7base[(i/5)%5];
			        group7bits[i*3+2] = group7base[i/25];
		        }
		        for(i=0;i<729;i++)
		        {
			        group10bits[i*3+0] = group10base[i%9];
			        group10bits[i*3+1] = group10base[(i/9)%9];
			        group10bits[i*3+2] = group10base[i/81];
		        }
	        }
        }

        static mp2dec_static_init __staticInit = new mp2dec_static_init();
    }

    internal struct mp2dec_bitstream
    {
        byte[] _data;
        int _dataOffset;
        int _bitOffs;
        int _totalDec;

        public mp2dec_bitstream(byte[] data, int dataOffset)
        {
            _data = data;
            _dataOffset = dataOffset;
            _bitOffs = 0;
            _totalDec = 0;
        }

        public uint Read(int bits)
        {
	        uint result = 0;
	        int resultBitOffs = bits;
	        int streamBitOffs = _bitOffs;
	        int availBits;
            byte[] bytes = _data;
            int bytesOffset = _dataOffset;
	        int originalBits = bits;	// Compiler optimization assist

	        this._totalDec += bits;

	        if(bits == 1)
	        {
                uint resultShifted = (uint)bytes[bytesOffset] & ((uint)1 << (7 - (streamBitOffs)));
		        result = (resultShifted >> (7 - (streamBitOffs)));
		        if(streamBitOffs == 7)
		        {
			        this._dataOffset = bytesOffset + 1;
			        this._bitOffs = 0;
		        }
		        else
			        this._bitOffs++;
		        return result;
	        }

	        if(streamBitOffs != 0)
	        {
		        availBits = 8 - streamBitOffs;
		        if(bits >= availBits)
		        {
			        // Longer than the first byte
			        resultBitOffs -= availBits;
			        result = ((uint)bytes[bytesOffset] & MP2Dec.BITMASK(0, availBits)) << resultBitOffs;
			        bits -= availBits;
			        streamBitOffs = 0;
			        bytesOffset++;
		        }
		        else
		        {
                    result = ((uint)bytes[bytesOffset] & MP2Dec.BITMASK((availBits - bits), bits)) >> (availBits - bits);
			        streamBitOffs += bits;

			        this._bitOffs = streamBitOffs;
			        return result;
		        }
	        }

	        if(originalBits >= 8)	// Compiler optimization assist
	        {
		        while(bits >= 8)
		        {
			        resultBitOffs -= 8;
			        result |= ((uint)bytes[bytesOffset] << resultBitOffs);
			        bits -= 8;
			        bytesOffset++;
		        }
	        }

	        if(bits != 0)
	        {
                result |= ((uint)bytes[bytesOffset] & MP2Dec.BITMASK(8 - bits, bits)) >> (8 - bits);
		        streamBitOffs = bits;
	        }

            this._dataOffset = bytesOffset;
            this._bitOffs = streamBitOffs;

	        return result;
        }
    }

    public struct MP2Header
    {
	    public ushort   syncword;				// 12 bits
	    public byte	    version;				// 1 bit
	    public byte	    layer;					// 2 bits
	    public bool	    disableCRCFlag;			// 1 bit
	    public byte	    bitrateIndex;			// 4 bits
	    public byte	    samplerateIndex;		// 2 bits
        public bool     padding;				// 1 bit
        public bool     privateFlag;			// 1 bit
	    public byte	    mode;					// 2 bits
	    public byte	    modeExt;				// 2 bits
        public bool     copyrightFlag;			// 1 bit
        public bool     originalFlag;			// 1 bit
	    public byte	    emphasis;				// 2 bits
    }

    public class MP2DecodeState
    {
        public const int FRAME_NUM_SAMPLES = 1152;

        public const int FRAME_BUFFER_SIZE_BYTES    = 4800;     // Maximum size of a frame in bytes, including header
        public const int FRAME_HEADER_SIZE_BYTES    = 4;        // Size of a frame header in bytes

        public uint FrameBytes { get { return this._frameBytes; } }
        public uint SampleRate { get { return this._sampleRate; } }
        public bool InputStereo { get { return this._inputStereo; } }

        uint _frameBytes;				// Number of bytes requested for frame data
	    uint tableIndex;
	    uint numSubBands;
	    uint stereoBound;
	    uint _sampleRate;
	    bool _inputStereo;

        DecodeArrays _darrays = new DecodeArrays();

	    int[] currentcalcbuffers = new int[MP2Dec.MAXCHANNEL];
	    int[] calcbufferoffsets = new int[MP2Dec.MAXCHANNEL];

	    FixedReal[][][] calcbuffers = new FixedReal[][][]     //[2][2][512]
            {
                new FixedReal[][]
                {
                    new FixedReal[512],
                    new FixedReal[512]
                },
                new FixedReal[][]
                {
                    new FixedReal[512],
                    new FixedReal[512]
                }
            };

        public MP2DecodeState()
        {
            for(int i=0;i<MP2Dec.MAXCHANNEL;i++)
            {
                currentcalcbuffers[i] = 0;
                calcbufferoffsets[i] = 15;
            }
        }

        public bool ParseHeader(byte[] inBytes, int inBytesOffset, out MP2Header outHeader)
        {
	        uint channelBitrate;
	        uint totalBitrate;

            mp2dec_bitstream bitstream = new mp2dec_bitstream(inBytes, inBytesOffset);

	        outHeader.syncword = (ushort)bitstream.Read(12);
	        outHeader.version = (byte)bitstream.Read(1);
	        outHeader.layer = (byte)bitstream.Read(2);
            outHeader.disableCRCFlag = (bitstream.Read(1) != 0);
	        outHeader.bitrateIndex = (byte)bitstream.Read(4);
	        outHeader.samplerateIndex = (byte)bitstream.Read(2);
	        outHeader.padding = (bitstream.Read(1) != 0);
	        outHeader.privateFlag = (bitstream.Read(1) != 0);
	        outHeader.mode = (byte)bitstream.Read(2);
	        outHeader.modeExt = (byte)bitstream.Read(2);
	        outHeader.copyrightFlag = (bitstream.Read(1) != 0);
	        outHeader.originalFlag = (bitstream.Read(1) != 0);
	        outHeader.emphasis = (byte)bitstream.Read(2);

	        _inputStereo = ((int)outHeader.mode != (int)MP2Dec.MpegMode.Single);

	        if(outHeader.syncword != 0xfff
		        || outHeader.bitrateIndex == 15
		        || outHeader.samplerateIndex == 3
		        || outHeader.layer != 2)
		        return false;

	        totalBitrate = channelBitrate = (uint)MP2Dec.tbl_bitrates[outHeader.version,outHeader.layer - 1,outHeader.bitrateIndex];
	        if(_inputStereo)
		        channelBitrate /= 2;

	        if(outHeader.bitrateIndex < 3)
		        tableIndex = 0;
	        else
		        tableIndex = 1;

	        this._sampleRate = (uint)MP2Dec.tbl_frequencies[outHeader.version,outHeader.samplerateIndex];

	        if(outHeader.version == (int)MP2Dec.MpegVersion.Mpeg1)
	        {
		        if(channelBitrate >= 56 &&
			        (_sampleRate == 48000 || channelBitrate <= 80))
			        numSubBands = 27;
		        else if(_sampleRate != 48000 && channelBitrate >= 96)
			        numSubBands = 30;
		        else if (_sampleRate != 32000 && channelBitrate <= 48)
			        numSubBands = 8;
		        else
			        numSubBands = 12;
	        }
	        else
		        numSubBands = 30;

	        if(outHeader.mode == (int)MP2Dec.MpegMode.Single)
		        stereoBound = 0;
	        else if(outHeader.mode == (int)MP2Dec.MpegMode.Joint)
		        stereoBound = ((uint)outHeader.modeExt + 1) << 2;
	        else
		        stereoBound = numSubBands;

	        if(stereoBound > numSubBands)
		        stereoBound = numSubBands;

	        // 1152 * 1000 / 8
	        // totalBitrate * 1000 * 1152/ sampleRate
	        this._frameBytes = (totalBitrate * 144000 / _sampleRate) - 4;
            if (outHeader.padding)
                _frameBytes++;

	        return true;
        }

        internal class DecodeArrays
        {
            static uint[][] JaggedUint(int d1, int d2)
            {
                uint[][] result = new uint[d1][];
                for (int i = 0; i < d1; i++)
                    result[i] = new uint[d2];
                return result;
            }

            static FixedReal[][] JaggedFixedReal(int d1, int d2)
            {
                FixedReal[][] result = new FixedReal[d1][];
                for (int i = 0; i < d1; i++)
                    result[i] = new FixedReal[d2];
                return result;
            }

            static FixedReal[][][] JaggedFixedReal(int d1, int d2, int d3)
            {
                FixedReal[][][] result = new FixedReal[d1][][];
                for (int i = 0; i < d1; i++)
                    result[i] = JaggedFixedReal(d2, d3);
                return result;
            }

            public FixedReal[][][] fraction;
            public uint[,] bitalloc;
            public uint[,] scaleselector;
            public FixedReal[,,] scalefactor;

            public FixedReal[,][] group;

            public uint[,] codelength;
            public FixedReal[,] factor;
            public FixedReal[,] c;
            public FixedReal[,] d;

            public int[] outputOffsets;

            public DecodeArrays()
            {
                this.fraction = JaggedFixedReal(MP2Dec.MAXCHANNEL, 3, MP2Dec.MAXSUBBAND);
                this.bitalloc = new uint[MP2Dec.MAXCHANNEL, MP2Dec.MAXSUBBAND];
                this.scaleselector = new uint[MP2Dec.MAXCHANNEL, MP2Dec.MAXSUBBAND];
                this.scalefactor = new FixedReal[2, 3, MP2Dec.MAXSUBBAND];

                this.group = new FixedReal[MP2Dec.MAXCHANNEL, MP2Dec.MAXSUBBAND][];

                this.codelength = new uint[MP2Dec.MAXCHANNEL, MP2Dec.MAXSUBBAND];
                this.factor = new FixedReal[MP2Dec.MAXCHANNEL, MP2Dec.MAXSUBBAND];
                this.c = new FixedReal[MP2Dec.MAXCHANNEL, MP2Dec.MAXSUBBAND];
                this.d = new FixedReal[MP2Dec.MAXCHANNEL, MP2Dec.MAXSUBBAND];

                this.outputOffsets = new int[MP2Dec.MAXCHANNEL];
            }
        }

        void DecodeFrame(MP2Header header, byte[] frameData, int frameDataOffset, int numChannels, short[][] output)
        {
            uint tableIndex = this.tableIndex;
	        uint numSubBands = this.numSubBands;
	        uint stereoBound = this.stereoBound;

	        mp2dec_bitstream bitstream = new mp2dec_bitstream(frameData, frameDataOffset);

            for(int i=0;i<MP2Dec.MAXCHANNEL;i++)
		        _darrays.outputOffsets[i] = 0;

	        if(!header.disableCRCFlag)
		        bitstream.Read(16);

	        if(numChannels == 1)
		        stereoBound = 0;	// Compiler assist

	        // Bitalloc
	        {
		        uint i;
		        int ch;
		        int[,] t = MP2Dec.tbl_bitalloclength;
                int t_0 = (int)tableIndex;
                int t_1 = 0;

		        for(i=0;i<stereoBound;i++,t_1++)
			        for(ch=0;ch<numChannels;ch++)
			        {
				        _darrays.bitalloc[ch,i] = bitstream.Read(t[t_0,t_1]);
			        }
		        for(;i<numSubBands;i++,t_1++)
		        {
			        uint l = bitstream.Read(t[t_0,t_1]);
			        for(ch=0;ch<numChannels;ch++)
				        _darrays.bitalloc[ch,i] = l;
		        }
	        }


	        // Scale selector
	        {
		        int ch;
		        uint i;
		        for(i=0;i<numSubBands;i++)
			        for(ch=0;ch<numChannels;ch++)
				        if(_darrays.bitalloc[ch,i] != 0)
				        {
					        _darrays.scaleselector[ch,i] = bitstream.Read(2);
				        }
	        }
	
	        // Scale index
	        {
		        for(int i=0;i<numSubBands;i++)
		        {
			        int ch;
			        for(ch=0;ch<numChannels;ch++)
			        {
				        int ba = (int)_darrays.bitalloc[ch,i];

				        if(ba != 0)
				        {
					        if(tableIndex == 0)
					        {
						        _darrays.group[ch,i]=MP2Dec.grouptableA[ba];
						        _darrays.codelength[ch,i]=MP2Dec.codelengthtableA[ba];
						        _darrays.factor[ch,i]=MP2Dec.factortableA[ba];
						        _darrays.c[ch,i]=MP2Dec.ctableA[ba];
						        _darrays.d[ch,i]=MP2Dec.dtableA[ba];
					        }
					        else
					        {
						        if(i <= 2)
						        {
							        _darrays.group[ch,i]=MP2Dec.grouptableB1[ba];
							        _darrays.codelength[ch,i]=MP2Dec.codelengthtableB1[ba];
							        _darrays.factor[ch,i]=MP2Dec.factortableB1[ba];
							        _darrays.c[ch,i]=MP2Dec.ctableB1[ba];
							        _darrays.d[ch,i]=MP2Dec.dtableB1[ba];
						        }
						        else
						        {
							        _darrays.group[ch,i] = MP2Dec.grouptableB234[ba];
							        if(i <= 10)
							        {
								        _darrays.codelength[ch,i] = MP2Dec.codelengthtableB2[ba];
								        _darrays.factor[ch,i] = MP2Dec.factortableB2[ba];
								        _darrays.c[ch,i] = MP2Dec.ctableB2[ba];
								        _darrays.d[ch,i] = MP2Dec.dtableB2[ba];
							        }
							        else if(i <= 22)
							        {
								        _darrays.codelength[ch,i]=MP2Dec.codelengthtableB3[ba];
								        _darrays.factor[ch,i]=MP2Dec.factortableB3[ba];
								        _darrays.c[ch,i]=MP2Dec.ctableB3[ba];
								        _darrays.d[ch,i]=MP2Dec.dtableB3[ba];
							        }
							        else
							        {
								        _darrays.codelength[ch,i]=MP2Dec.codelengthtableB4[ba];
								        _darrays.factor[ch,i]=MP2Dec.factortableB4[ba];
								        _darrays.c[ch,i]=MP2Dec.ctableB4[ba];
								        _darrays.d[ch,i]=MP2Dec.dtableB4[ba];
							        }
						        }
					        }

					        switch(_darrays.scaleselector[ch,i])
					        {
					        case 0:
						        {
							        uint bits = bitstream.Read(18);
							        _darrays.scalefactor[ch,0,i] = MP2Dec.scalefactorstable[(bits & MP2Dec.BITMASK(12, 6)) >> 12];
							        _darrays.scalefactor[ch,1,i] = MP2Dec.scalefactorstable[(bits & MP2Dec.BITMASK(6, 6)) >> 6];
							        _darrays.scalefactor[ch,2,i] = MP2Dec.scalefactorstable[(bits & MP2Dec.BITMASK(0, 6))];
						        }
						        break;
					        case 1:
						        {
							        uint bits = bitstream.Read(12);
							        _darrays.scalefactor[ch,0,i]=
								        _darrays.scalefactor[ch,1,i]=MP2Dec.scalefactorstable[(bits & MP2Dec.BITMASK(6, 6)) >> 6];
							        _darrays.scalefactor[ch,2,i]=MP2Dec.scalefactorstable[(bits & MP2Dec.BITMASK(0, 6))];
						        }
						        break;
					        case 2:
						        {
							        uint bits = bitstream.Read(6);
							        _darrays.scalefactor[ch,0,i]=
							           _darrays.scalefactor[ch,1,i]=
							           _darrays.scalefactor[ch,2,i]=MP2Dec.scalefactorstable[bits];
						        }
						        break;
					        case 3:
						        {
							        uint bits = bitstream.Read(12);
							        _darrays.scalefactor[ch,0,i]=MP2Dec.scalefactorstable[(bits & MP2Dec.BITMASK(6, 6)) >> 6];
							        _darrays.scalefactor[ch,1,i]=
								        _darrays.scalefactor[ch,2,i]=MP2Dec.scalefactorstable[(bits & MP2Dec.BITMASK(0, 6))];
						        }
						        break;
					        }
				        }
			        }	// ch
		        }	//i
	        }

	        // Read Sample
	        {
		        int block;

		        for(block=0;block<MP2Dec.SCALEBLOCK;block++)
		        {
			        // Read Sample
			        for(uint i=0;i<numSubBands;i++)
			        {
				        int ch;
				        for(ch=0;ch<numChannels;ch++)
				        {
					        if(i >= stereoBound && ch > 0)
					        {
						        int j;
						        for(j=0;j<3;j++)
							        _darrays.fraction[ch][j][i] = _darrays.fraction[0][j][i];
						        continue;
					        }

					        if(_darrays.bitalloc[ch,i] != 0)
					        {
						        if(_darrays.group[ch,i] != null)
						        {
                                    FixedReal[] sPtr;

							        uint code = bitstream.Read((int)_darrays.codelength[ch,i]);

							        code += code << 1;
							        sPtr = _darrays.group[ch,i];

							        _darrays.fraction[ch][0][i] = sPtr[code+0];
							        _darrays.fraction[ch][1][i] = sPtr[code+1];
							        _darrays.fraction[ch][2][i] = sPtr[code+2];
						        }
						        else
						        {
							        int l = (int)_darrays.codelength[ch,i];
							        uint bits = bitstream.Read(l*3);
                                    FixedReal chiFactor = _darrays.factor[ch,i];

							        _darrays.fraction[ch][0][i] = FixedReal.ShortScale(
                                        (bits & MP2Dec.BITMASK(l*2, l)) >> (l*2), chiFactor);
							        _darrays.fraction[ch][1][i] = FixedReal.ShortScale(
								        (bits & MP2Dec.BITMASK(l, l)) >> l, chiFactor);
							        _darrays.fraction[ch][2][i] = FixedReal.ShortScale(
								        (bits & MP2Dec.BITMASK(0, l)), chiFactor);
						        }
					        }
					        else
						        _darrays.fraction[ch][0][i] = _darrays.fraction[ch][1][i] = _darrays.fraction[ch][2][i] = new FixedReal(0.0);
				        }
			        }

			        //Fraction
			        {
				        for(int ch=0;ch<numChannels;ch++)
				        {
					        for(uint i=0;i<numSubBands;i++)
					        {
						        if(_darrays.bitalloc[ch,i] != 0)
						        {
							        FixedReal t = _darrays.scalefactor[ch,block/4,i];

							        if(_darrays.group[ch,i] == null)
							        {
								        _darrays.fraction[ch][0][i] = (_darrays.fraction[ch][0][i] + _darrays.d[ch,i]) * _darrays.c[ch,i];
								        _darrays.fraction[ch][1][i] = (_darrays.fraction[ch][1][i] + _darrays.d[ch,i]) * _darrays.c[ch,i];
								        _darrays.fraction[ch][2][i] = (_darrays.fraction[ch][2][i] + _darrays.d[ch,i]) * _darrays.c[ch,i];
							        }

							        _darrays.fraction[ch][0][i] *= t;
							        _darrays.fraction[ch][1][i] *= t;
							        _darrays.fraction[ch][2][i] *= t;
						        }
					        }
				        }
			        }

                    /*
        	        for(int i=0;i<3;i++)
					    for(int ch=0;ch<numChannels;ch++)
                            Array.Clear(_darrays.fraction[ch][i], (int)numSubBands, MP2Dec.MAXSUBBAND - (int)numSubBands);
                     */

                    for (int i = (int)numSubBands; i < MP2Dec.MAXSUBBAND; i++)
                    {
                        for (int ch = 0; ch < numChannels; ch++)
                            _darrays.fraction[ch][0][i] = _darrays.fraction[ch][1][i] = _darrays.fraction[ch][2][i] = new FixedReal(0.0);
                    }

			        for(int i=0;i<3;i++)
					    for(int ch=0;ch<numChannels;ch++)
						    MP2Dec.subbandsynthesis(_darrays.fraction[ch][i], ref this.currentcalcbuffers[ch], ref this.calcbufferoffsets[ch],
                                calcbuffers[ch], output[ch], ref _darrays.outputOffsets[ch]);
		        }
	        }
        }

        public void DecodeFrame(MP2Header header, byte[] frameData, int frameOffset, short[][] output)
        {
            DecodeFrame(header, frameData, frameOffset, _inputStereo ? 2 : 1, output);
        }
    }
}
